/*
 * This file is part of Cadmium.
 * Copyright (C) 2007-2010 Xavier Clerc.
 *
 * Cadmium is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * Cadmium is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package fr.x9c.cadmium.kernel;

import java.util.HashMap;
import java.util.Map;

/**
 * This class implements an exception to be thrown in replacement of Java
 * program exit, to allow several interpreted programs to coexist within the
 * same JVM (as the end of one of these programs should not entail exit from
 * JVM). <br/>
 * To ensure that all threads depending from the same context "exit" with the
 * same code, each time an instance is created using the public constructor,
 * <i>(context, exit_code)</i> is added to an underlying map that is used
 * by {@link #createFromContext(fr.x9c.cadmium.kernel.Context)} to build an
 * instance with correct exit code. This method may throw a
 * {@link fr.x9c.cadmium.kernel.Fail.Exception} if an asynchronous exception
 * has been registered with the context.
 *
 * @author <a href="mailto:cadmium@x9c.fr">Xavier Clerc</a>
 * @version 1.0
 * @since 1.0
 */
public final class FalseExit extends Exception {

    /** Serialization identifier. */
    static final long serialVersionUID = 5590723243562579220L;

    /** Default exit code. */
    private static final int DEFAULT_EXIT_CODE = 0;

    /**
     * Map from contexts to exit codes. <br/>
     * Used to ensure that all threads from the same context "exit" with the
     * same code. <br/>
     * Used by {@link #createFromContext(fr.x9c.cadmium.kernel.Context)}.
     */
    private static final Map<Context, Integer> EXIT_CODES =
        new HashMap<Context, Integer>();

    /** Exit code of the interpreted program. */
    private final int exitCode;

    /**
     * Constructs a "false exit" from an exit code.
     * @param ec exit code of the interpreted program
     */
    private FalseExit(final int ec) {
        this.exitCode = ec;
    } // end constructor(int)

    /**
     * Constructs a "false exit" from a context and exit code. <br/>
     * The exit code is registered with the context such that
     * {@link #createFromContext(fr.x9c.cadmium.kernel.Context)} called with
     * the passed context will return an exception with the passed exit code.
     * @param ctxt context - should not be <tt>null</tt>
     * @param ec exit code of the interpreted program
     */
    public FalseExit(final Context ctxt, final int ec) {
        this(ec);
        assert ctxt != null : "null ctxt";
        FalseExit.EXIT_CODES.put(ctxt, ec);
    } // end constructor(Context, int)

    /**
     * Returns the exit code of the interpreted program.
     * @return the exit code of the interpreted program
     */
    public int getExitCode() {
        return this.exitCode;
    } // end method 'getExitCode()'

    /**
     * Creates and return an instance from context, the exit code being
     * retrieved from the underlying map such as its exit code is the one
     * registered with the context.
     * @param ctxt context to return exception for - should not be <tt>null</tt>
     * @return an instance whose exit code is retrieved from the underlying map
     * @throws Fail.Exception if an asynchronous exception has been set
     */
    public static FalseExit createFromContext(final Context ctxt)
        throws Fail.Exception {
        assert ctxt != null : "null ctxt";
        final Exception asyncException = ctxt.getAndClearAsyncException();
        if ((asyncException != null)
            && (asyncException instanceof Fail.Exception)) {
            throw (Fail.Exception) asyncException;
        } // end if
        final Integer res = FalseExit.EXIT_CODES.get(ctxt);
        return new FalseExit(res != null ? res.intValue() : FalseExit.DEFAULT_EXIT_CODE);
    } // end method 'createFromContext(Context)'

} // end class 'FalseExit'
